"""
CRUD 基类
提供通用的增删改查操作，自动注入 user_id 实现数据隔离

这是防止水平越权攻击的关键！
所有查询都会自动带上 user_id 过滤条件，确保 A 用户无法访问 B 用户的数据
"""
from typing import Generic, TypeVar, Dict, Any, List, Optional
from fastapi import HTTPException, status
from motor.motor_asyncio import AsyncIOMotorDatabase
from bson import ObjectId
from datetime import datetime

ModelType = TypeVar("ModelType")

class CRUDBase(Generic[ModelType]):
    """
    CRUD 基类
    
    所有数据操作自动注入 user_id，实现数据权限隔离
    
    使用示例：
        note_crud = CRUDBase("notes")
        
        # 创建（自动添加 user_id）
        note = await note_crud.create(db, {"title": "My Note"}, current_user["id"])
        
        # 查询（自动过滤 user_id）
        notes = await note_crud.get_all_by_user(db, current_user["id"])
        
        # 单条查询（自动验证 user_id）
        note = await note_crud.get_one(db, note_id, current_user["id"])
    """
    
    def __init__(self, collection_name: str):
        """
        初始化 CRUD 基类
        
        Args:
            collection_name: MongoDB 集合名称
        """
        self.collection_name = collection_name
    
    async def create(
        self,
        db: AsyncIOMotorDatabase,
        obj_in: Dict[str, Any],
        user_id: str
    ) -> Dict[str, Any]:
        """
        创建新记录（自动注入 user_id）
        
        Args:
            db: 数据库连接
            obj_in: 要创建的对象数据
            user_id: 当前用户 ID（自动注入到数据中）
            
        Returns:
            创建的对象（包含 id 字段）
        """
        obj_data = obj_in.copy()
        
        # 关键：自动注入 user_id
        obj_data["user_id"] = user_id
        obj_data["created_at"] = datetime.utcnow()
        obj_data["updated_at"] = None
        
        result = await db[self.collection_name].insert_one(obj_data)
        
        # 转换 _id 为 id
        obj_data["id"] = str(result.inserted_id)
        obj_data.pop("_id", None)
        
        return obj_data
    
    async def get_all_by_user(
        self,
        db: AsyncIOMotorDatabase,
        user_id: str,
        skip: int = 0,
        limit: int = 100
    ) -> List[Dict[str, Any]]:
        """
        获取用户的所有记录（自动过滤 user_id）
        
        Args:
            db: 数据库连接
            user_id: 当前用户 ID（自动作为过滤条件）
            skip: 跳过的记录数（分页）
            limit: 返回的最大记录数（分页）
            
        Returns:
            用户的记录列表
        """
        # 关键：自动添加 user_id 过滤条件
        cursor = db[self.collection_name].find({"user_id": user_id}).skip(skip).limit(limit)
        items = await cursor.to_list(length=limit)
        
        # 转换 _id 为 id
        for item in items:
            item["id"] = str(item.pop("_id"))
        
        return items
    
    async def get_one(
        self,
        db: AsyncIOMotorDatabase,
        id: str,
        user_id: str
    ) -> Dict[str, Any]:
        """
        获取单条记录（自动验证 user_id）
        
        这是防止越权的关键：即使知道记录 ID，也必须是该用户的记录才能访问
        
        Args:
            db: 数据库连接
            id: 记录 ID
            user_id: 当前用户 ID（自动验证所有权）
            
        Returns:
            记录数据
            
        Raises:
            HTTPException: 记录不存在或无权访问（404）
        """
        try:
            oid = ObjectId(id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="无效的 ID 格式"
            )
        
        # 关键：同时检查 _id 和 user_id
        obj = await db[self.collection_name].find_one({
            "_id": oid,
            "user_id": user_id  # 防止越权访问
        })
        
        if not obj:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="记录不存在或无权访问"
            )
        
        obj["id"] = str(obj.pop("_id"))
        return obj
    
    async def update_one(
        self,
        db: AsyncIOMotorDatabase,
        id: str,
        data: Dict[str, Any],
        user_id: str
    ) -> Dict[str, Any]:
        """
        更新记录（自动验证 user_id）
        
        Args:
            db: 数据库连接
            id: 记录 ID
            data: 要更新的数据
            user_id: 当前用户 ID（自动验证所有权）
            
        Returns:
            更新后的记录
            
        Raises:
            HTTPException: 记录不存在或无权访问（404）
        """
        try:
            oid = ObjectId(id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="无效的 ID 格式"
            )
        
        # 添加更新时间
        update_data = data.copy()
        update_data["updated_at"] = datetime.utcnow()
        
        # 关键：同时匹配 _id 和 user_id
        result = await db[self.collection_name].update_one(
            {"_id": oid, "user_id": user_id},  # 防止越权修改
            {"$set": update_data}
        )
        
        if result.matched_count == 0:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="记录不存在或无权访问"
            )
        
        # 返回更新后的对象
        return await self.get_one(db, id, user_id)
    
    async def delete_one(
        self,
        db: AsyncIOMotorDatabase,
        id: str,
        user_id: str
    ) -> Dict[str, str]:
        """
        删除记录（自动验证 user_id）
        
        Args:
            db: 数据库连接
            id: 记录 ID
            user_id: 当前用户 ID（自动验证所有权）
            
        Returns:
            删除结果
            
        Raises:
            HTTPException: 记录不存在或无权访问（404）
        """
        try:
            oid = ObjectId(id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="无效的 ID 格式"
            )
        
        # 关键：同时匹配 _id 和 user_id
        result = await db[self.collection_name].delete_one({
            "_id": oid,
            "user_id": user_id  # 防止越权删除
        })
        
        if result.deleted_count == 0:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="记录不存在或无权访问"
            )
        
        return {"message": "删除成功", "id": id}
    
    async def count_by_user(
        self,
        db: AsyncIOMotorDatabase,
        user_id: str
    ) -> int:
        """
        统计用户的记录数量
        
        Args:
            db: 数据库连接
            user_id: 当前用户 ID
            
        Returns:
            记录数量
        """
        count = await db[self.collection_name].count_documents({"user_id": user_id})
        return count

